// 17_terminal_cube.rs
// 不使用第三方2D/3D引擎，在终端中显示一个旋转的正立方体

use std::thread::sleep;
use std::time::Duration;

// 将原来的 main 函数改为公共的 run 函数
pub fn run() {
    // 清除屏幕
    println!("{esc}[2J{esc}[1;1H", esc = 27 as char);
    
    // 旋转角度
    let mut angle_x = 0.0;
    let mut angle_y = 0.0;
    
    // 旋转速度
    let rotation_speed_x = 0.04;
    let rotation_speed_y = 0.03;
    
    // 帧率控制
    let frame_delay = Duration::from_millis(50);
    
    // 主循环
    loop {
        // 清除屏幕
        print!("{esc}[2J{esc}[1;1H", esc = 27 as char);
        
        // 绘制立方体
        draw_cube(angle_x, angle_y);
        
        // 更新旋转角度
        angle_x += rotation_speed_x;
        angle_y += rotation_speed_y;
        
        // 控制帧率
        sleep(frame_delay);
        
        // 按Ctrl+C退出程序
        // 注意：在某些终端环境中，可能需要手动终止程序
    }
}

// 绘制立方体函数
fn draw_cube(angle_x: f64, angle_y: f64) {
    // 屏幕尺寸（字符终端的宽和高）
    const WIDTH: usize = 40;
    const HEIGHT: usize = 20;
    
    // 清除屏幕缓冲区
    let mut screen = vec![vec![' '; WIDTH]; HEIGHT];
    let mut z_buffer = vec![vec![-f64::INFINITY; WIDTH]; HEIGHT];
    
    // 立方体的8个顶点坐标
    let vertices = [
        [-1.0, -1.0, -1.0], // 0
        [1.0, -1.0, -1.0],  // 1
        [1.0, 1.0, -1.0],   // 2
        [-1.0, 1.0, -1.0],  // 3
        [-1.0, -1.0, 1.0],  // 4
        [1.0, -1.0, 1.0],   // 5
        [1.0, 1.0, 1.0],    // 6
        [-1.0, 1.0, 1.0],   // 7
    ];
    
    // 立方体的12条边（连接的顶点索引）
    let edges = [
        (0, 1), (1, 2), (2, 3), (3, 0), // 前面
        (4, 5), (5, 6), (6, 7), (7, 4), // 后面
        (0, 4), (1, 5), (2, 6), (3, 7), // 连接前面和后面的边
    ];
    
    // 投影后的顶点坐标
    let mut projected = [[0.0; 2]; 8];
    
    // 对每个顶点进行旋转和投影
    for i in 0..8 {
        let (x, y, z) = rotate_point(vertices[i][0], vertices[i][1], vertices[i][2], angle_x, angle_y);
        
        // 简单的透视投影
        let scale = 5.0 / (3.0 - z); // 5.0是缩放因子，3.0是相机到原点的距离
        let sx = x * scale + WIDTH as f64 / 2.0;
        let sy = y * scale + HEIGHT as f64 / 2.0;
        
        // 检查点是否在屏幕范围内
        if sx >= 0.0 && sx < WIDTH as f64 && sy >= 0.0 && sy < HEIGHT as f64 {
            let (x, y) = (sx as usize, sy as usize);
            // 更新z-buffer，只保留最前面的点
            if z > z_buffer[y][x] {
                z_buffer[y][x] = z;
                screen[y][x] = get_depth_char(z);
            }
            
            // 保存投影坐标用于绘制边
            projected[i][0] = sx;
            projected[i][1] = sy;
        }
    }
    
    // 绘制边
    for &(a, b) in &edges {
        draw_line(&mut screen, &mut z_buffer, 
                 projected[a][0] as i32, projected[a][1] as i32, 
                 projected[b][0] as i32, projected[b][1] as i32);
    }
    
    // 打印屏幕内容
    for row in &screen {
        for &c in row {
            print!("{}", c);
        }
        println!();
    }
    
    // 显示使用说明
    println!("按 Ctrl+C 退出程序");
}

// 旋转点的函数
fn rotate_point(x: f64, y: f64, z: f64, angle_x: f64, angle_y: f64) -> (f64, f64, f64) {
    // 绕Y轴旋转
    let cos_y = angle_y.cos();
    let sin_y = angle_y.sin();
    let temp_x = x * cos_y - z * sin_y;
    let temp_z = z * cos_y + x * sin_y;
    
    // 绕X轴旋转
    let cos_x = angle_x.cos();
    let sin_x = angle_x.sin();
    let temp_y = y * cos_x - temp_z * sin_x;
    let temp_z = temp_z * cos_x + y * sin_x;
    
    (temp_x, temp_y, temp_z)
}

// 根据深度获取字符的函数（用于模拟3D效果）
fn get_depth_char(z: f64) -> char {
    // 根据z值选择不同的字符来表示深度
    // 字符的密度从低到高： .,:-=+*#%@
    const CHARS: &str = ".,:;!~*=#%@";
    let depth = (z + 2.0) / 4.0; // 将z值映射到0-1范围
    let index = (depth * CHARS.len() as f64) as usize;
    CHARS.chars().nth(index.min(CHARS.len() - 1)).unwrap_or(' ')
}

// 绘制线的函数（使用Bresenham算法）
fn draw_line(
    screen: &mut Vec<Vec<char>>,
    z_buffer: &mut Vec<Vec<f64>>,
    x0: i32, y0: i32, x1: i32, y1: i32
) {
    let width = screen[0].len() as i32;
    let height = screen.len() as i32;
    
    // 检查起点和终点是否在屏幕范围内
    if x0 < 0 || x0 >= width || y0 < 0 || y0 >= height ||
       x1 < 0 || x1 >= width || y1 < 0 || y1 >= height {
        return;
    }
    
    let dx = (x1 - x0).abs();
    let dy = (y1 - y0).abs();
    let sx = if x0 < x1 { 1 } else { -1 };
    let sy = if y0 < y1 { 1 } else { -1 };
    
    let mut err = dx - dy;
    let mut x = x0;
    let mut y = y0;
    
    // 中点Bresenham算法绘制直线
    loop {
        let (xi, yi) = (x as usize, y as usize);
        
        // 检查点是否在屏幕范围内
        if xi < screen[0].len() && yi < screen.len() {
            // 更新点的字符（使用*表示边）
            if z_buffer[yi][xi] > -f64::INFINITY {
                screen[yi][xi] = '*';
            }
        }
        
        // 检查是否到达终点
        if x == x1 && y == y1 {
            break;
        }
        
        let e2 = 2 * err;
        if e2 > -dy {
            err -= dy;
            x += sx;
        }
        if e2 < dx {
            err += dx;
            y += sy;
        }
    }
}